This topic provides background for creating and examining messages using the MailMessage object.
There are three ways to create a MailMessage object in Mail for .NET:
The first two methods involve decoding existing messages from some other format. This topic will focus on the third method - creating a message from scratch.
Most mail messages consist of three major components: the headers, the text message, and attachments. This section examines these components.
The header fields (headers) contain important information related to the content of the message. The MailMessage object contains properties for required and commonly used header fields.
Recipients: There are three properties to set recipients - To, Cc and Bcc. Set the appropriate property to specify a recipient. To is for recipients directly affected by the message. Cc (Carbon Copy) is for recipients who should see the message but are not directly affected. Bcc (Blind Carbon Copy) is for recipients who should be hidden from other recipients (these recipients will receive the message, but other recipients will not know they have).
Sender: Set the From property to specify the sender of the message.
Subject: Set the Subject property to specify the message subject. Typically this is a summary of the content.
The MailMessage also has properties for Priority, ReplyTo and Date, but it is generally not required or necessary to change their default values.
Set MailMessage.Text to add unformatted text to the message. The "HTML Messages" section below discusses HTML text.
Attachments are included files that may be saved to disk when a message is received. To add an attachment to a message, create an Attachment object using a local file path, and add it to the MailMessage.Attachments collection.
This example creates a simple text message with one attachment.
C# |
Copy Code |
---|---|
private MailMessage CreateMailMessageWithAttachment() { MailMessage message = new MailMessage(); message.To = "to@dart.com"; message.From = "from@dart.com"; message.Subject = "This is a simple message"; message.Text = "Please see the attached file."; message.Attachments.Add(new Attachment("C:\\File\\myAttachment.jpg")); return message; } |
Visual Basic |
Copy Code |
---|---|
Private Function CreateMailMessageWithAttachment() As MailMessage Dim message As New MailMessage() message.To = "to@dart.com" message.From = "from@dart.com" message.Subject = "This is a simple message" message.Text = "Please see the attached file." message.Attachments.Add(New Attachment("C:\File\myAttachment.jpg")) Return message End Function |
The Basics section above demonstrates how to add standard headers to a message using properties of the MailMessage object. MailMessage also includes a Headers property which can be used to add custom header lines to the message. Note that all message Parts include the Headers property (and can therefore have customized headers), not just the MailMessage object.
MailMessage.Parts holds all Parts in the MailMessage, including Textparts (which have string content), Attachments and Resources (which have FileInfo or byte[] content), and Multiparts (which have their own Multipart.Parts collection). The hierarchical structure of a MailMessage is exposed by nested Multipart.Parts collections.
Note that a MailMessage or Multipart with a single Part will encode itself by removing the Multipart "level". In other words, MailMessage will never generate an encoded message containing a Multipart with only one child Part.
This example demonstrates custom Headers and the Multipart.Parts collection. Custom header fields are added to the MailMessage and its Parts. Textparts are added to a Multipart, which is in turn added to the MailMessage. A Resource part is also added to the MailMessage.
C# |
Copy Code |
---|---|
private MailMessage addCustomParts(MailMessage message) { //Add custom header field to message message.Headers.Add("X-Alt-Message", new HeaderField("X-Alt-Message", "Message with Alternatives")); //Create and add alternative parts to a multipart part Textpart part1 = new Textpart("This is alternative 1."); part1.Headers.Add("X-Alt", new HeaderField("X-Alt", "Alternative 1")); Textpart part2 = new Textpart("This is alternative 2."); part2.Headers.Add("X-Alt", new HeaderField("X-Alt", "Alternative 2")); Multipart multipart = new Multipart(Multipart.Alternative); multipart.Parts.Add(part1); multipart.Parts.Add(part2); //Add a MultiPart to the MailMessage message.Parts.Add(multipart); //Add a Resource part to the MailMessage Resource resource = new Resource(new FileStream(Application.StartupPath + "\\myImage.jpg", FileMode.Open), "myImage.jpg"); resource.ContentType = new ContentType("image/jpeg"); resource.ContentId = "<myImage>"; message.Parts.Add(resource); return message; } |
Visual Basic |
Copy Code |
---|---|
Private Function addCustomParts(ByVal message As MailMessage) As MailMessage 'Add custom header field to message message.Headers.Add("X-Alt-Message", New HeaderField("X-Alt-Message", "Message with Alternatives")) 'Create and add alternative parts to a multipart part Dim part1 As New Textpart("This is alternative 1.") part1.Headers.Add("X-Alt", New HeaderField("X-Alt", "Alternative 1")) Dim part2 As New Textpart("This is alternative 2.") part2.Headers.Add("X-Alt", New HeaderField("X-Alt", "Alternative 2")) Dim multipart As New Multipart(Dart.Mail.Multipart.Alternative) multipart.Parts.Add(part1) multipart.Parts.Add(part2) 'Add a MultiPart to the MailMessage message.Parts.Add(multipart) 'Add a Resource part to the MailMessage Dim resource As New Resource(New FileStream(Application.StartupPath & "\myImage.jpg", FileMode.Open), "myImage.jpg") resource.ContentType = New ContentType("image/jpeg") resource.ContentId = "<myImage>" message.Parts.Add(resource) Return message End Function |
HTML is used in messages that benefit from formatted text and images. This method adds a multipart/related part to an existing multipart/alternative message.
C# |
Copy Code |
---|---|
private MailMessage addHtml(MailMessage message) { string html = "This is my HTML message. It contains a couple of images." + "<img alt='image1.jpg' src='image1.jpg' />" + "<img alt='image2.jpg' src='image2.jpg' />"; message.Parts.Add(new Multipart(null, html, new DirectoryInfo("C:\\Html Resources\\images").GetFiles())); return message; } |
Visual Basic |
Copy Code |
---|---|
Private Function addHtml(ByVal message As MailMessage) As MailMessage Dim html As String = "This is my HTML message. It contains a couple of images." & "<img alt='image1.jpg' src='image1.jpg' />" & "<img alt='image2.jpg' src='image2.jpg' />" message.Parts.Add(New Multipart(Nothing, html, New DirectoryInfo("C:\Html Resources\images").GetFiles())) Return message End Function |
PowerTCP Mail for .NET features "on-the-fly" encoding/decoding for a minimal memory/disk footprint. Encoding occurs when the Smtp component Sends a MailMessage to a mail server, when Save stores the message to disk or a Stream, and when ToString transforms the message to an encoded string.
All messages (including non-MIME messages) decode to MailMessage objects with a MIME structure. A MIME message contains parts which may hold data or parts of their own. The Parts property uses Filter to access different types of parts, including Attachment, Resource, Multipart, Textpart and Htmlpart, with a recursive option.
All Parts contain a Headers property, which is a Dictionary indexed by HeaderField.Name (for example, "Content-Type"). Many header fields are accessible as properties on the Part itself. For example, Textpart includes ContentType and ContentTransferEncoding properties. This is true for MailMessage as well (To, From, etc. See "Creating a Basic Message" above).
Parts provide a Content property to access the part's contents. Textpart.Content is a String, whereas Attachment.Content is a FileInfo and Resource.Content is a byte[]. MailMessage and Multipart objects contain a Parts collection instead of a Content property.
When a message is decoded, attachments are saved to Attachment.Directory, or stored in memory when the static Attachment.DecodeToMemory is true. When Attachment.DecodeToMemory is false, Attachment.Content.MoveTo() can be used to move the attachment to a permanent location. Attachment.GetContentStream() may be used to access the stream containing the file data when Attachment.DecodeToMemory is true. MailMessage.Attachments returns a flat list of all attachments contained in the email.
Inline files (parts with a Content Disposition header of 'inline') are represented by the Resource class and are only decoded to memory. Resource.Content returns a byte array containing the file data.
C# |
Copy Code |
---|---|
private void SaveAttachments(MailMessage message, string saveDirectory) { foreach (Attachment attachment in message.Attachments) attachment.Content.CopyTo(System.IO.Path.Combine(saveDirectory, attachment.FileName)); } //If Attachment.DecodeToMemory was set to true in your application: private void SaveAttachmentsFromMemory(MailMessage message, string saveDirectory) { //Stream.CopyTo() used here is only available in .NET 4.0 and up foreach (Attachment attachment in message.Attachments) using (FileStream fs = new FileStream(Path.Combine(saveDirectory, attachment.FileName), FileMode.CreateNew, FileAccess.Write)) attachment.GetContentStream().CopyTo(fs); } //Saves inline files (files intended for display within the HTML message body) private void SaveInlineFiles(MailMessage message, string saveDirectory) { foreach (Resource res in message.Resources) using (FileStream fs = new FileStream(Path.Combine(saveDirectory, res.ContentType.Name), FileMode.CreateNew, FileAccess.Write)) fs.Write(res.Content, 0, res.Content.Length); } |
Visual Basic |
Copy Code |
---|---|
Private Sub SaveAttachments(ByVal message As MailMessage, ByVal saveDirectory As String) For Each attachment As Attachment In message.Attachments attachment.Content.CopyTo(System.IO.Path.Combine(saveDirectory, attachment.FileName)) Next attachment End Sub 'If Attachment.DecodeToMemory was set to true in your application: Private Sub SaveAttachmentsFromMemory(ByVal message As MailMessage, ByVal saveDirectory As String) 'Stream.CopyTo() used here is only available in .NET 4.0 and up For Each attachment As Attachment In message.Attachments Using fs As New FileStream(Path.Combine(saveDirectory, attachment.FileName), FileMode.CreateNew, FileAccess.Write) attachment.GetContentStream().CopyTo(fs) End Using Next attachment End Sub 'Saves inline files (files intended for display within the HTML message body) Private Sub SaveInlineFiles(ByVal message As MailMessage, ByVal saveDirectory As String) For Each res As Resource In message.Resources Using fs As New FileStream(Path.Combine(saveDirectory, res.ContentType.Name), FileMode.CreateNew, FileAccess.Write) fs.Write(res.Content, 0, res.Content.Length) End Using Next res End Sub |
Use MailMessage.Clone to create an exact copy of a MailMessage. Use a MailMessage as a template to create new messages or to create "Reply" or "Forward" messages as demonstrated in the code below.
C# |
Copy Code |
---|---|
private MailMessage createCustomReplyMessage(MailMessage originalMessage, string replyText, string fromAddress) { //Clone the original message MailMessage replyMessage = originalMessage.Clone() as MailMessage; //Set the recipient address if (!String.IsNullOrWhiteSpace(replyMessage.ReplyTo)) replyMessage.To = replyMessage.ReplyTo; else if (!String.IsNullOrWhiteSpace(replyMessage.From)) replyMessage.To = replyMessage.From; //Set the sender's address replyMessage.From = fromAddress; //Indicate that this is a reply in the subject replyMessage.Subject = "Re: " + originalMessage.Subject; //Clear some properties of the message replyMessage.ReplyTo = ""; replyMessage.Cc = ""; replyMessage.Sender = ""; //Set the message text to be the reply followed by some of the original //message's header fields and the original text string messageText = replyText + Environment.NewLine + Environment.NewLine; messageText += "From: " + originalMessage.From + Environment.NewLine; messageText += "Sent: " + originalMessage.Date.ToString() + Environment.NewLine; messageText += "To: " + originalMessage.To + Environment.NewLine; messageText += "Subject: " + originalMessage.Subject + Environment.NewLine + Environment.NewLine; //Set the plain text replyMessage.Text = messageText + originalMessage.Text; //Set the HTML text string html = originalMessage.Html; //Locate place in HTML string to insert reply text int textStartIndex = html.ToLower().IndexOf("<body"); if (textStartIndex == -1) textStartIndex = html.ToLower().IndexOf("<html"); textStartIndex = html.IndexOf(">", textStartIndex) + 1; //Insert reply text into HTML string and set the Body.Html messageText = "<div style=\"color:black\">" + messageText.Replace("\r\n", "<br />") + "</div>"; html = html.Insert(textStartIndex, messageText); replyMessage.Html = html; return replyMessage; } |
Visual Basic |
Copy Code |
---|---|
Private Function createCustomReplyMessage(ByVal originalMessage As MailMessage, ByVal replyText As String, ByVal fromAddress As String) As MailMessage 'Clone the original message Dim replyMessage As MailMessage = TryCast(originalMessage.Clone(), MailMessage) 'Set the recipient address If Not String.IsNullOrWhiteSpace(replyMessage.ReplyTo) Then replyMessage.To = replyMessage.ReplyTo ElseIf Not String.IsNullOrWhiteSpace(replyMessage.From) Then replyMessage.To = replyMessage.From End If 'Set the sender's address replyMessage.From = fromAddress 'Indicate that this is a reply in the subject replyMessage.Subject = "Re: " & originalMessage.Subject 'Clear some properties of the message replyMessage.ReplyTo = "" replyMessage.Cc = "" replyMessage.Sender = "" 'Set the message text to be the reply followed by some of the original 'message's header fields and the original text Dim messageText As String = replyText & Environment.NewLine & Environment.NewLine messageText &= "From: " & originalMessage.From & Environment.NewLine messageText &= "Sent: " & originalMessage.Date.ToString() & Environment.NewLine messageText &= "To: " & originalMessage.To & Environment.NewLine messageText &= "Subject: " & originalMessage.Subject & Environment.NewLine & Environment.NewLine 'Set the plain text replyMessage.Text = messageText & originalMessage.Text 'Set the HTML text Dim html As String = originalMessage.Html 'Locate place in HTML string to insert reply text Dim textStartIndex As Integer = html.ToLower().IndexOf("<body") If textStartIndex = -1 Then textStartIndex = html.ToLower().IndexOf("<html") End If textStartIndex = html.IndexOf(">", textStartIndex) + 1 'Insert reply text into HTML string and set the Body.Html messageText = "<div style=""color:black"">" & messageText.Replace(vbCrLf, "<br />") & "</div>" html = html.Insert(textStartIndex, messageText) replyMessage.Html = html Return replyMessage End Function |
The S/MIME Overview page provides a background on signing and encrypting messages using S/MIME.
MailMessage provides three methods for encoding and decoding S/MIME messages. SecureSign signs a message using a certificate specifically issued for the sender's email address. SecureEncrypt encrypts a message using a certificate issued for the recipient's email address. SecureDecode decodes a signed and/or encrypted message. All three methods have a parameterless overload that automatically searches the appropriate certificate store for a matching email certificate. The code below demonstrates each method overload with parameters.
C# |
Copy Code |
---|---|
private MailMessage getSignedMessage(MailMessage message) { //Find the signing certificate in the "CurrentUser/My" certificate store //The following code results in the same signed message as "message.SecureSign();" X509Store myPersonalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); myPersonalStore.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 certificate in myPersonalStore.Certificates) { if (certificate.Subject.Contains("E=" + message.From.ToString())) { //Sign the message message.SecureSign(certificate, X509IncludeOption.ExcludeRoot, DigestAlgorithm.Sha1, true, false); return message; } } return null; } |
C# |
Copy Code |
---|---|
private MailMessage getEncryptedMessage(MailMessage message) { //Find the encrypting certificate in the CurrentUser/AddressBook store //The following code results in the same encrypted message as "message.SecureEncrypt();" X509Certificate2Collection encryptingCertificates = new X509Certificate2Collection(); X509Store addressBookStore = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser); addressBookStore.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 certificate in addressBookStore.Certificates) { if (certificate.Subject.Contains("E=" + message.To)) { encryptingCertificates.Add(certificate); //Encrypt the message message.SecureEncrypt(encryptingCertificates, EncryptingAlgorithm.TripleDes, false); return message; } } return null; } |
C# |
Copy Code |
---|---|
private MailMessage getDecodedMessage(MailMessage message) { //Load the decrypting certificate from an exported certificate file (may also be loaded from an X509Store) X509Certificate2 decryptingCertificate = new X509Certificate2(Application.StartupPath + "\\myCertificate.pfx"); //Decode the message and import the signing certificate into the AddressBook certificate Store //(If the decrypting certificate is already present in the "MY" certificate store, //the parameterless SecureDecode() may be used instead.) SignerInfoCollection signatories = message.SecureDecode(new X509Certificate2Collection(decryptingCertificate), true); //Optionally verify the signature and validate the certificate foreach (SignerInfo signator in signatories) signator.CheckSignature(false); return message; } |
Visual Basic |
Copy Code |
---|---|
Private Function getSignedMessage(ByVal message As MailMessage) As MailMessage 'Find the signing certificate in the "CurrentUser/My" certificate store 'The following code results in the same signed message as "message.SecureSign();" Dim myPersonalStore As New X509Store(StoreName.My, StoreLocation.CurrentUser) myPersonalStore.Open(OpenFlags.ReadOnly) For Each certificate As X509Certificate2 In myPersonalStore.Certificates If certificate.Subject.Contains("E=" & message.From.ToString()) Then 'Sign the message message.SecureSign(certificate, X509IncludeOption.ExcludeRoot, DigestAlgorithm.Sha1, True, False) Return message End If Next certificate Return Nothing End Function |
Visual Basic |
Copy Code |
---|---|
Private Function getEncryptedMessage(ByVal message As MailMessage) As MailMessage 'Find the encrypting certificate in the CurrentUser/AddressBook store 'The following code results in the same encrypted message as "message.SecureEncrypt();" Dim encryptingCertificates As New X509Certificate2Collection() Dim addressBookStore As New X509Store(StoreName.AddressBook, StoreLocation.CurrentUser) addressBookStore.Open(OpenFlags.ReadOnly) For Each certificate As X509Certificate2 In addressBookStore.Certificates If certificate.Subject.Contains("E=" & message.To) Then encryptingCertificates.Add(certificate) 'Encrypt the message message.SecureEncrypt(encryptingCertificates, EncryptingAlgorithm.TripleDes, False) Return message End If Next certificate Return Nothing End Function |
Visual Basic |
Copy Code |
---|---|
Private Function getDecodedMessage(ByVal message As MailMessage) As MailMessage 'Load the decrypting certificate from an exported certificate file (may also be loaded from an X509Store) Dim decryptingCertificate As New X509Certificate2(Application.StartupPath & "\myCertificate.pfx") 'Decode the message and import the signing certificate into the AddressBook certificate Store '(If the decrypting certificate is already present in the "MY" certificate store, 'the parameterless SecureDecode() may be used instead.) Dim signatories As SignerInfoCollection = message.SecureDecode(New X509Certificate2Collection(decryptingCertificate), True) 'Optionally verify the signature and validate the certificate For Each signator As SignerInfo In signatories signator.CheckSignature(False) Next signator Return message End Function |